package cn.btks.backstagemanage.user.service.impl;

import cn.btks.backstagemanage.unit.pojo.Unit;
import cn.btks.backstagemanage.unit.repository.UnitRepository;
import cn.btks.backstagemanage.user.pojo.LoginRecord;
import cn.btks.backstagemanage.user.pojo.User;
import cn.btks.backstagemanage.user.repository.LoginRecordRepository;
import cn.btks.backstagemanage.user.repository.UserRepository;
import cn.btks.backstagemanage.user.service.UserService;
import cn.btks.commonserver.pojo.PageList;
import cn.btks.commonserver.result.ResultState;
import cn.btks.commonserver.result.StateInfo;
import cn.btks.commonserver.util.sm2.SM2EncDecUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * @Author lijun
 * @Date 2021/1/11 17:48
 */
@Service
@Transactional
public class UserLocalServiceImpl implements UserService {

    @Autowired
    UserRepository userRepository;

    @Autowired
    LoginRecordRepository loginRecordRepository;

    @Autowired
    UnitRepository unitRepository;

//    @Autowired
//    PasswordEncoder passwordEncoder;

    @Override
    public User findByUserName(String userName) {
        try {
            User user = decryptTele(userRepository.findByUsername(userName));
            return user;
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public User findById(Long id) {
        Optional<User> optional = userRepository.findById(id);
        if (optional.isPresent()) {
            try {
                User user = decryptTele(optional.get());
                return user;
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }

    @Override
    public ResultState findPageList(Integer page, Integer pageSize, String userName, String name, Long unitId) {
        PageRequest pageRequest = PageRequest.of(page - 1, pageSize);
        Specification<User> specification = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root,
                                         CriteriaQuery<?> criteriaQuery,
                                         CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                if (!StringUtils.isEmpty(userName)) {
                    predicates.add(criteriaBuilder.like(root.get("username"),
                            "%" + userName + "%"));
                }
                if (!StringUtils.isEmpty(name)) {
                    predicates.add(criteriaBuilder.like(root.get("name"),
                            "%" + name + "%"));
                }
                if (unitId != null) {
                    predicates.add(criteriaBuilder.equal(root.get("unitId"), unitId));
                }
                Predicate[] predicates1 = new Predicate[predicates.size()];
                return criteriaBuilder.and(predicates.toArray(predicates1));
            }
        };
        Page<User> configPage = userRepository.findAll(specification, pageRequest);
        List<User> users = new ArrayList<>();
        try {
            users = decryptTeleList(configPage.getContent());
        } catch (Exception e) {

        }
        if (configPage.getTotalElements() > 0) {
            PageList pageList = new PageList();
            pageList.setResult(users);
            pageList.setPageNum(configPage.getTotalPages());
            pageList.setTotalCount(configPage.getTotalElements());
            return new ResultState(StateInfo.FIND_OK.getCode(), StateInfo.FIND_OK.getMsg(), pageList);
        }
        return new ResultState(StateInfo.FIND_NULL.getCode(), StateInfo.FIND_NULL.getMsg());
    }

    @Override
    public ResultState findPageList1(Integer page, Integer pageSize, String userName, String name, Long unitId, Long cityId) {
        PageRequest pageRequest = PageRequest.of(page - 1, pageSize);
        Specification<User> specification = new Specification<User>() {
            @Override
            public Predicate toPredicate(Root<User> root,
                                         CriteriaQuery<?> criteriaQuery,
                                         CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                if (!StringUtils.isEmpty(userName)) {
                    predicates.add(criteriaBuilder.like(root.get("username"),
                            "%" + userName + "%"));
                }
                if (!StringUtils.isEmpty(name)) {
                    predicates.add(criteriaBuilder.like(root.get("name"),
                            "%" + name + "%"));
                }
                List<Unit> unitList = unitRepository.findAllByCityId(cityId);
                if (unitList != null && !unitList.isEmpty()) {
                    long[] unitIds = unitList.stream().mapToLong(s -> s.getId()).toArray();
                    if (unitIds != null && unitIds.length > 0) {
                        if (cityId != null) {
                            Path<Object> path = root.get("unitId");//定义查询的字段
                            CriteriaBuilder.In<Object> in = criteriaBuilder.in(path);
                            for (int i = 0; i < unitIds.length; i++) {
                                in.value(unitIds[i]);//存入值
                            }
                            predicates.add(criteriaBuilder.and(criteriaBuilder.and(in)));//存入条件集合里
                        }
                    }
                }
                if (unitId != null) {
                    predicates.add(criteriaBuilder.equal(root.get("unitId"), unitId));
                }
                Predicate[] predicates1 = new Predicate[predicates.size()];
                return criteriaBuilder.and(predicates.toArray(predicates1));
            }
        };
        Page<User> configPage = userRepository.findAll(specification, pageRequest);
        List<User> users = new ArrayList<>();
        try {
            users = decryptTeleList(configPage.getContent());
        } catch (Exception e) {

        }
        if (configPage.getTotalElements() > 0) {
            PageList pageList = new PageList();
            pageList.setResult(users);
            pageList.setPageNum(configPage.getTotalPages());
            pageList.setTotalCount(configPage.getTotalElements());
            return new ResultState(StateInfo.FIND_OK.getCode(), StateInfo.FIND_OK.getMsg(), pageList);
        }
        return new ResultState(StateInfo.FIND_NULL.getCode(), StateInfo.FIND_NULL.getMsg());
    }

    @Override
    public ResultState save(User user) throws IOException {
        User user2 = userRepository.findByUsername(user.getUsername());
        if (user2 != null) {
            return new ResultState(StateInfo.SAVE_ERROR4.getCode(), StateInfo.SAVE_ERROR4.getMsg());
        }
        String password = SM2EncDecUtils.encrypt(user.getPassword());
        user.setPassword(password);
        if (!StringUtils.isEmpty(user.getTelephone())) {
            user.setTelephone(SM2EncDecUtils.encrypt(user.getTelephone()));
        }
        User user1 = userRepository.save(user);
        return user1 != null ? new ResultState(StateInfo.SAVE_OK.getCode(), StateInfo.SAVE_OK.getMsg(), user1) :
                new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
    }

    @Override
    public ResultState update(User user) throws IOException {
        String password = "";
        if (user.getId() != null) {
            User user1 = userRepository.findById(user.getId()).get();
            if (user1 != null) {
                password = user1.getPassword();
                user.setPassword(password);
            }
        } else {
            return new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
        }
        if (!StringUtils.isEmpty(user.getTelephone())) {
            user.setTelephone(SM2EncDecUtils.encrypt(user.getTelephone()));
        }
        User user1 = userRepository.save(user);
        if (user1 != null) {
            return new ResultState(StateInfo.SAVE_OK.getCode(), StateInfo.SAVE_OK.getMsg(), user1);
        }
        return new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
    }

    @Override
    public ResultState delete(Long id) {
        Optional<User> optional = userRepository.findById(id);
        if (optional.isPresent()) {
            int userRolesCount = userRepository.queryCountUserRoles(id);
            if (userRolesCount > 0) {
                userRepository.deleteUserRolesByUserId(id);
            }
            int result = userRepository.deleteUserById(id);
            return result > 0 ? new ResultState(StateInfo.DELETE_OK.getCode(), StateInfo.DELETE_OK.getMsg(), result) :
                    new ResultState(StateInfo.DELETE_ERROR.getCode(), StateInfo.DELETE_ERROR.getMsg());
        }
        return new ResultState(StateInfo.DELETE_ERROR_2.getCode(), StateInfo.DELETE_ERROR_2.getMsg());
    }

    @Override
    public ResultState deleteConnection(Long id) {
        Optional<User> optional = userRepository.findById(id);
        if (optional.isPresent()) {
            int userRolesCount = userRepository.queryCountUserRoles(id);
            if (userRolesCount > 0) {
                int result = userRepository.deleteUserRolesByUserId(id);
                return result > 0 ? new ResultState(StateInfo.DELETE_OK.getCode(), StateInfo.DELETE_OK.getMsg(), result) :
                        new ResultState(StateInfo.DELETE_ERROR.getCode(), StateInfo.DELETE_ERROR.getMsg());
            }
        }
        return new ResultState(StateInfo.DELETE_ERROR_2.getCode(), StateInfo.DELETE_ERROR_2.getMsg());
    }

    @Override
    public ResultState updatePassword(String oldPassword, String newPassword, Long id) throws IOException {
        Optional<User> optional = userRepository.findById(id);
        if (optional.isPresent()) {
            User user = optional.get();
            String decryptPassword = SM2EncDecUtils.decrypt(user.getPassword());
            if (decryptPassword.equals(oldPassword)) {
                String encryptPassword = SM2EncDecUtils.encrypt(newPassword);
                int result = userRepository.updatePassword(encryptPassword, id);
                return result > 0 ? new ResultState(StateInfo.PASSWORD_UPDATE_OK.getCode(), StateInfo.PASSWORD_UPDATE_OK.getMsg()) :
                        new ResultState(StateInfo.PASSWORD_UPDATE_ERROR.getCode(), StateInfo.PASSWORD_UPDATE_ERROR.getMsg());
            } else {
                new ResultState(StateInfo.PASSWORD_UPDATE_ERROR1.getCode(), StateInfo.PASSWORD_UPDATE_ERROR1.getMsg());
            }
        }
        return new ResultState(StateInfo.PASSWORD_UPDATE_ERROR2.getCode(), StateInfo.PASSWORD_UPDATE_ERROR2.getMsg());
    }

    @Override
    public void recordLoginCount(String userName, String name) {
        LoginRecord loginRecord = new LoginRecord();
        loginRecord.setUserName(userName);
        loginRecord.setName(name);
        loginRecord.setLoginTime(new Date());
        loginRecordRepository.save(loginRecord);
    }

    @Override
    public int getLoginRecord() {
        List<LoginRecord> all = loginRecordRepository.findAll();
        return all.size();
    }

    private User decryptTele(User user) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        User user1 = objectMapper.readValue(objectMapper.writeValueAsString(user), User.class);
        if (!StringUtils.isEmpty(user.getTelephone())) {
            user1.setTelephone(SM2EncDecUtils.decrypt(user.getTelephone()));
        }
        return user1;
    }

    private List<User> decryptTeleList(List<User> userList) throws IOException {
        List<User> userList1 = new ArrayList<>();
        if (!CollectionUtils.isEmpty(userList)) {
            for (User item : userList) {
                User user = decryptTele(item);
                userList1.add(user);
            }
        }
        return userList1;
    }
}
